home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Communications
/
PPPMonitor1.16
/
Source
/
ExecRunCommand.m
< prev
next >
Wrap
Text File
|
1996-01-29
|
11KB
|
445 lines
// -------------------------------------------------------------------------------------
// ExecRunCommand.m
// (Indent:4, Tabs:4)
// -------------------------------------------------------------------------------------
// Copyright 1996 Persistent Technologies, Inc. - all rights reserved
// -------------------------------------------------------------------------------------
// This source code comes with no warranty of any kind, and the user assumes all
// responsibility for its use.
// -------------------------------------------------------------------------------------
#import <appkit/appkit.h>
#import <libc.h>
#import <mach/cthreads.h>
#import <stdlib.h>
#import <stdarg.h>
#import <string.h>
#import <pwd.h>
#import <sys/types.h>
#import <sys/wait.h>
#import "ExecRunCommand.h"
// -------------------------------------------------------------------------------------
// pending pid list - keep track of child process creation and termination
/* pid list */
#define MAX_PIDLIST_SIZE 64 /* maximum concurrent child processes */
typedef struct {
int pid;
int didExit;
union wait status;
} pidStatus_t;
static pidStatus_t pidList[MAX_PIDLIST_SIZE] = { 0 };
static int pidCount = 0;
/* return pidList index of specified pid number */
static int _pid_index(int pid)
{
if (pid > 0) {
int n;
for (n = 0; n < pidCount; n++) { if (pid == pidList[n].pid) return n; }
}
return -1; // not found
}
/* add pid to list (does not check to see if it already exists!) */
static int _pid_add(int thePid)
{
int ndx;
for (ndx = 0; (ndx < pidCount) && pidList[ndx].pid; ndx++);
pidList[ndx].pid = thePid;
pidList[ndx].didExit = NO;
if (ndx == pidCount) pidCount++; // upper bounds (MAX_PIDLIST_SIZE) not checked
return ndx;
}
/* wait for pid to exit (keep track of all pid exit reports) */
static BOOL _pid_wait(int thePid, union wait *rtnStat)
{
int pidNdx = _pid_index(thePid);
if (pidNdx < 0) pidNdx = _pid_add(thePid); //(ERROR) NOT FOUND - add anyway
/* wait for pid to exit */
if (!(pidList[pidNdx].didExit)) {
for (;;) {
union wait status;
int pid = wait(&status);
if (pid == -1) { pidNdx = 0; break; };
pidNdx = _pid_index(pid);
if (pidNdx < 0) pidNdx = _pid_add(pid); //(ERROR) NOT FOUND - add anyway
pidList[pidNdx].didExit = YES;
pidList[pidNdx].status = status;
if (thePid == pid) break;
}
}
/* return reported status */
if (pidNdx >= 0) {
*rtnStat = pidList[pidNdx].status;
pidList[pidNdx].pid = 0;
return YES;
}
return NO;
}
// -------------------------------------------------------------------------------------
// ExecRunCommand private methods
@interface ExecRunCommand(Private)
+ (struct passwd*)_getpwnam:(const char*)user;
+ (BOOL)_isRoot;
+ (int)_setenv:(char**)ep :(char*)eVal :(char*)fmt, ...;
- (BOOL)_runCommand:(const char*)command user:(const char*)user;
- (void)_sendSignal:(int)sigval;
- (void)_stopCommand;
- (int)_popen:(const char*)cmd user:(const char*)user;
- (int)_pclose;
- (void)_gotData;
static void gotData(int fd, void *self);
- (void)_commandOutput:(const char*)buf len:(int)len;
@end
// --------------------------------------------------------------------------------
@implementation ExecRunCommand
// --------------------------------------------------------------------------------
/* initialize */
- init
{
[super init];
cmdChild = 0;
inputDescriptor = 0;
delegate = nil;
tag = 0;
return self;
}
/* free */
- _free:sender { return [self free]; }
- free
{
if (cmdChild > 0) {
fprintf(stderr,"cannot free yet, killing command ...\n");
[self killCommand];
[self perform:@selector(_free:) with:self afterDelay:500 cancelPrevious:YES];
return (id)nil;
}
[super free];
return (id)nil;
}
// --------------------------------------------------------------------------------
/* run command */
+ runCommand:(const char*)command user:(const char*)user output:(id)theDelegate
{
BOOL didStart;
ExecRunCommand *mySelf = [[self alloc] init];
[mySelf setDelegate:theDelegate];
didStart = [mySelf _runCommand:command user:user];
if (!didStart) { [mySelf free]; mySelf = (id)nil; }
return mySelf;
}
/* run command */
+ runCommand:(const char*)command output:(id)theDelegate
{
return [ExecRunCommand runCommand:command user:(char*)nil output:theDelegate];
}
/* similar to "system(...)" */
+ (int)system:(const char*)command user:(const char*)user output:(id)theDelegate
{
int err, infd;
ExecRunCommand *mySelf = [[self alloc] init];
[mySelf setDelegate:theDelegate];
infd = [mySelf _popen:command user:user];
if (infd >= 0) {
int cnt;
char buffer[1025];
do {
cnt = read(infd, buffer, sizeof(buffer));
if (cnt != -1) [mySelf _commandOutput:buffer len:cnt];
} while (cnt > 0);
err = [mySelf _pclose];
close(infd);
} else err = RUNCMD_EXEC; // could not execute
mySelf->cmdChild = 0;
[mySelf free];
return err;
}
/* see "system:user:output:" */
+ (int)system:(const char*)command user:(const char*)user
{
return [self system:command user:user output:nil];
}
/* see "system:user:output:" */
+ (int)system:(const char*)command
{
return [self system:command user:(char*)nil output:nil];
}
/* set tag */
- setTag:(int)theTag
{
tag = theTag;
return self;
}
/* return tag */
- (int)tag
{
return tag;
}
/* return true if command is still active */
- (BOOL)isActive
{
return (cmdChild > 0)? YES : NO;
}
/* set delegate */
- setDelegate:(id)theDelegate
{
delegate = theDelegate;
return self;
}
/* return delegate */
- (id)delegate
{
return delegate;
}
/* interrupt command */
- interruptCommand
{
[self _sendSignal:SIGINT];
return self;
}
/* terminate command */
- terminateCommand
{
[self _sendSignal:SIGTERM];
return self;
}
/* kill command */
- killCommand
{
[self _sendSignal:SIGKILL];
return self;
}
/* return true if running with effective user root */
+ (BOOL)isRunningAsRoot
{
return [self _isRoot];
}
// --------------------------------------------------------------------------------
/* wrapper for getpwnam() */
+ (struct passwd*)_getpwnam:(const char*)user
{
extern void _lu_setport(port_t);
extern port_t _lookupd_port(int);
_lu_setport(_lookupd_port(0)); // may not be necessary post v3.0
return user? getpwnam(user) : getpwuid(getuid());
}
/* remote: return 'root' flag */
+ (BOOL)_isRoot
{
return ([ExecRunCommand _getpwnam:"root"]->pw_uid == geteuid())? YES : NO;
}
/* set environment variable */
+ (int)_setenv:(char**)ep :(char*)eVal :(char*)fmt, ...
{
va_list args;
register char *cp, *dp;
va_start(args, fmt);
vsprintf(eVal, fmt, args);
va_end(args);
for (;dp = *ep; ep++) {
for (cp = eVal; (*cp == *dp) && (*cp != '=') && *cp; cp++, dp++) continue;
if (((*cp == '=') || !*cp) && ((*dp == '=') || !*dp)) { *ep = eVal; return 0; }
}
return -1;
}
// --------------------------------------------------------------------------------
/* execute a command */
- (BOOL)_runCommand:(const char*)command user:(const char*)user
{
inputDescriptor = [self _popen:command user:user];
if (inputDescriptor >= 0) {
DPSAddFD(inputDescriptor, gotData, self, NX_BASETHRESHOLD);
return YES;
}
return NO;
}
/* send signal */
- (void)_sendSignal:(int)sigval
{
if (cmdChild > 0) {
killpg(cmdChild, sigval);
kill(cmdChild, sigval);
}
}
/* stop command */
- (void)_stopCommand
{
int error;
DPSRemoveFD(inputDescriptor);
error = [self _pclose];
close(inputDescriptor);
[self commandDidCompleteWithError:error];
}
/* open pipe to shell and execute command */
- (int)_popen:(const char*)cmd user:(const char*)user
{
int inputP[2], hisOutput, myInput;
const char **locEnv = environ;
/* only "root" can specify a user */
if (user && ![[self class] _isRoot]) return -1;
/* "root" user commands must be preceeded with "{root}..." */
if (user && !strcmp(user,"root")) {
const char *match = "{root}";
int matchLen = strlen(match);
if (strncmp(cmd,match,matchLen)) return -1;
cmd += matchLen;
}
/* open pipe */
pipe(inputP);
myInput = inputP[0];
hisOutput = inputP[1];
/* fork and execute shell */
if ((cmdChild = vfork()) == 0) {
int i;
char **env;
setpgrp(0, getpid());
/* set up pipe handles */
close(myInput);
if (hisOutput != 1) dup2(hisOutput, 1);
if (hisOutput != 2) dup2(hisOutput, 2);
if ((hisOutput != 1) && (hisOutput != 2)) close(hisOutput);
/* make local copy of environment table */
for (i = 0; locEnv[i]; i++);
env = (char**)alloca(sizeof(char*) * (i + 2 + 1)); // allocate on stack
memset(env, 0, sizeof(char*) * (i + 2 + 1));
memcpy(env, locEnv, sizeof(char*) * i);
/* switch to user */
if ([ExecRunCommand _isRoot]) {
struct passwd *pw = [ExecRunCommand _getpwnam:user];
if ((setgid(pw->pw_gid) < 0) ||
(initgroups(pw->pw_name,pw->pw_gid)) ||
(setuid(pw->pw_uid) < 0)) {
_exit(RUNCMD_USER); // cannot switch to user
}
[ExecRunCommand _setenv:env:(char*)alloca(strlen(pw->pw_dir)+7)
:"HOME=%s",pw->pw_dir];
[ExecRunCommand _setenv:env:(char*)alloca(strlen(pw->pw_name)+7)
:"USER=%s",pw->pw_name];
}
/* execute command */
// execle("/bin/csh","csh","-f","-c",cmd,(char*)nil,env);
execle("/bin/sh","sh","-c",cmd,(char*)nil,env);
_exit(RUNCMD_EXEC);
}
/* set io */
if (cmdChild == -1) { close(myInput); myInput = -1; } else _pid_add(cmdChild);
close(hisOutput);
return myInput;
}
/* close shell command pipe */
- (int)_pclose
{
union wait status;
int omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
BOOL flag = _pid_wait(cmdChild,&status);
(void)sigsetmask(omask);
if (!flag) return 0; // no children
if (status.w_status & 0xFF) return RUNCMD_STOPPED; // process was terminated
return (status.w_status >> 8) & 0xFF;
}
/* filter for data piped from command shell */
- (void)_gotData
{
char data[1024];
int n, cnt = 0;
/* read available text */
do {
if ((n = read(inputDescriptor,data,sizeof(data))) >= 0) {
cnt += n;
if (n) [self _commandOutput:data len:n];
}
} while (n == sizeof(data));
/* stop command when done */
if (!cnt) [self _stopCommand];
}
/* fd routine for receiving data */
static void gotData(int fd, void *self)
{
[(ExecRunCommand*)self _gotData];
}
// --------------------------------------------------------------------------------
// support for remote shell server output
/* copy text to scrollView (MAIN THREAD ONLY!) */
- (void)_commandOutput:(const char*)buf len:(int)len
{
if (len && buf) {
if (delegate && [delegate respondsTo:@selector(commandOutput:buffer:len:)]) {
[delegate commandOutput:self buffer:buf len:len];
}
}
}
/* copy text to scrollView (MAIN THREAD ONLY!) */
- (oneway void)commandOutput:(const char*)buf len:(int)len
{
if (len && buf) {
[self _commandOutput:buf len:len];
free((char*)buf);
}
}
/* indicate that the shell has completed */
- (oneway void)commandDidCompleteWithError:(int)errorCode;
{
if (delegate && [delegate respondsTo:@selector(commandDidComplete:withError:)]) {
[delegate commandDidComplete:self withError:errorCode];
}
cmdChild = 0;
[self free];
}
@end